package com.weseal.google.utils;

import com.google.zxing.WriterException;
import org.apache.commons.codec.binary.Base32;
import org.apache.commons.codec.binary.Base64;
import org.junit.Test;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.awt.image.BufferedImage;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

/**
 * @author weseal
 * @title
 * @description
 * @usage
 * @copyright Copyright 2014  rrmj Corporation. All rights reserved.
 * @company 上海众多美网络科技有限公司
 * @create 2017/10/19
 */
public class AuthenticatorUtils {


	public static final int SECRET_SIZE = 10;

	public static final String SEED = "g8GjEvTbW5oVSV7avLBdwIHqGlUYNzKFI7izOF8GwLDVKs2m0QN7vxRs2im5MDaNCWGmcD2rvcZx";

	public static final String RANDOM_NUMBER_ALGORITHM = "SHA1PRNG";

	private int windowSize = 3; //最多可偏移的时间

	private void setWindowSize(int s) {
		if (s >= 1 && s <= 17) {
			windowSize = s;
		}
	}

	//生成安全密钥
	public static String generateSecretKey() throws NoSuchAlgorithmException {
		SecureRandom secureRandom = null;
		secureRandom = SecureRandom.getInstance(RANDOM_NUMBER_ALGORITHM);
		secureRandom.setSeed(Base64.decodeBase64(SEED));
		byte[] buffer = secureRandom.generateSeed(SECRET_SIZE);
		Base32 codec = new Base32();
		byte[] bEncodedKey = codec.encode(buffer);
		return new String(bEncodedKey);
	}

	public static BufferedImage getQRcodeBufferedImage(String user, String secret) throws WriterException, NoSuchAlgorithmException {
		String format = "otpauth://totp/%s?secret=%s";
		return QRUtils.generateMatrixBufferedImage(String.format(format, user, secret));
	}


	private boolean checkCode(String secret, long code, long timeMsec) throws InvalidKeyException, NoSuchAlgorithmException {
		Base32 codec = new Base32();
		byte[] decodedKey = codec.decode(secret);
		long t = (timeMsec / 1000L) / 30L;

		for (int i = 0; i <= windowSize; i++) {
			long hash;
			hash = verifyCode(decodedKey, t + i);
			if (hash == code) {
				return true;
			}
		}
		return false;
	}

	private static int verifyCode(byte[] key, long t) throws NoSuchAlgorithmException, InvalidKeyException {
		byte[] data = new byte[8];
		long value = t;
		for (int i = 8; i-- > 0; value >>>= 8) {
			data[i] = (byte) value;
		}

		SecretKeySpec signKey = new SecretKeySpec(key, "HmacSHA1");
		Mac mac = Mac.getInstance("HmacSHA1");
		mac.init(signKey);
		byte[] hash = mac.doFinal(data);
		int offset = hash[20 - 1] & 0xF;
		long truncatedHash = 0;
		for (int i = 0; i < 4; i++) {
			truncatedHash <<= 8;
			truncatedHash |= (hash[offset + i] & 0xFF);
		}
		truncatedHash &= 0x7FFFFFFF;
		truncatedHash %= 1000000;
		return (int) truncatedHash;
	}


	public static boolean authCode(String userCode, String savedSecret) {
		long code = Long.parseLong(userCode);
		long t = System.currentTimeMillis();
		AuthenticatorUtils ga = new AuthenticatorUtils();
		ga.setWindowSize(1);
		boolean r = false;
		try {
			r = ga.checkCode(savedSecret, code, t);

		} catch (InvalidKeyException e) {
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		return r;
	}


	@Test
	public void authCode() {
		System.out.println(AuthenticatorUtils.authCode("905594", "2HGT4WXWDHERV5TF"));
	}


}
